package cn.chiship.sdk.core.util.http;

import cn.chiship.sdk.core.base.BaseResult;
import cn.chiship.sdk.core.base.constants.BaseConstants;
import cn.chiship.sdk.core.enums.ContentTypeEnum;
import cn.chiship.sdk.core.enums.HeaderEnum;
import cn.chiship.sdk.core.enums.StatusEnum;
import cn.chiship.sdk.core.exception.custom.BusinessException;
import cn.chiship.sdk.core.util.ObjectUtil;
import cn.chiship.sdk.core.util.StringUtil;
import cn.chiship.sdk.core.util.UrlUtil;
import com.alibaba.fastjson.JSON;
import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 网络工具
 *
 * @author lijian
 */
public class HttpUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtil.class);

    private static final String NO_Q = "无效的请求!";

    private static final String A = "https://";

    private static HttpUtil instance;

    /**
     * 消息头
     */
    private Map<String, String> headers;

    private HttpUtil() {

    }

    /**
     * 获得实例
     *
     * @return HttpUtil
     */
    public synchronized static HttpUtil getInstance() {
        if (instance == null) {
            instance = new HttpUtil();
        }
        return instance;
    }

    /**
     * 构建消息头
     *
     * @param headers 消息头
     * @return HttpUtil
     */
    public HttpUtil headers(Map<String, String> headers) {
        this.headers = headers;
        return this;
    }

    /**
     * 发送get请求
     *
     * @param url 服务路径
     * @return BaseResult
     */
    public BaseResult doGet(String url) {
        return doGet(url, null);
    }

    /**
     * 发送get请求
     *
     * @param url     服务路径
     * @param queries 参数
     * @return BaseResult
     */
    public BaseResult doGet(String url, Map<String, Object> queries) {
        try {
            HttpClient httpClient = wrapClient(url);
            HttpGet request = new HttpGet(UrlUtil.buildUrl(url, queries));
            if (!StringUtil.isNull(headers)) {
                for (Map.Entry<String, String> e : headers.entrySet()) {
                    request.addHeader(e.getKey(), e.getValue());
                }
            }
            return analysisHttpResponse(httpClient.execute(request));
        } catch (IOException e) {
            throw new BusinessException(NO_Q);
        }
    }

    /**
     * post请求 application/x-www-form-urlencoded
     *
     * @param url    服务路径
     * @param bodies body请求参数
     * @return BaseResult
     */
    public BaseResult doPostForm(String url, Map<String, Object> bodies) {
        return doPostForm(url, null, bodies);
    }

    /**
     * post请求 application/x-www-form-urlencoded
     *
     * @param url     服务路径
     * @param queries 参数
     * @param bodies  body请求参数
     * @return BaseResult
     */
    public BaseResult doPostForm(String url, Map<String, Object> queries, Map<String, Object> bodies) {
        try {
            HttpClient httpClient = wrapClient(url);
            HttpPost request = new HttpPost(UrlUtil.buildUrl(url, queries));
            if (!StringUtil.isNull(headers)) {
                for (Map.Entry<String, String> e : headers.entrySet()) {
                    request.addHeader(e.getKey(), e.getValue());
                }
            }
            if (!StringUtil.isNull(bodies)) {
                List<NameValuePair> nameValuePairList = new ArrayList<>();
                for (Map.Entry<String, Object> entry : bodies.entrySet()) {
                    nameValuePairList.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
                }
                UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, StandardCharsets.UTF_8);
                formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
                request.setEntity(formEntity);
            }
            return analysisHttpResponse(httpClient.execute(request));
        } catch (IOException e) {
            throw new BusinessException(NO_Q);
        }
    }

    /**
     * post application/json
     *
     * @param url  服务路径
     * @param body json参数body体
     * @return BaseResult
     */
    public BaseResult doPost(String url, Map<String, Object> body) {
        return doPost(url, null, JSON.toJSONString(body));
    }

    /**
     * post application/json
     *
     * @param url     服务路径
     * @param queries 请求参数
     * @param body    json参数body体
     * @return BaseResult
     */
    public BaseResult doPost(String url, Map<String, Object> queries, Map<String, Object> body) {
        return doPost(url, queries, JSON.toJSONString(body));
    }

    /**
     * post application/json
     *
     * @param url    服务路径
     * @param bodies json数组参数body体
     * @return BaseResult
     */
    public BaseResult doPost(String url, List<Map<String, Object>> bodies) {
        return doPost(url, null, JSON.toJSONString(bodies));
    }

    /**
     * post application/json
     *
     * @param url     服务路径
     * @param queries 请求参数
     * @param bodies  json数组参数body体
     * @return BaseResult
     */
    public BaseResult doPost(String url, Map<String, Object> queries, List<Map<String, Object>> bodies) {
        return doPost(url, queries, JSON.toJSONString(bodies));
    }

    /**
     * post application/json
     *
     * @param url  服务路径
     * @param body 字符串参数body体
     * @return BaseResult
     */
    public BaseResult doPost(String url, String body) {
        return doPost(url, null, body);
    }

    public BaseResult doPost(String url, Map<String, Object> queries, String body) {
        return doPost(url, queries, body, "utf-8");
    }

    /**
     * post application/json
     *
     * @param url     服务路径
     * @param queries 请求参数
     * @param body    字符串参数body体
     * @return BaseResult
     */
    public BaseResult doPost(String url, Map<String, Object> queries, String body, String encoding) {
        try {
            HttpClient httpClient = wrapClient(url);
            HttpPost request = new HttpPost(UrlUtil.buildUrl(url, queries));
            if (!StringUtil.isNull(headers)) {
                for (Map.Entry<String, String> e : headers.entrySet()) {
                    request.addHeader(e.getKey(), e.getValue());
                }
            }
            request.addHeader(HttpHeaders.CONTENT_TYPE, "application/json;charset=utf-8");
            request.setHeader(HttpHeaders.ACCEPT, "application/json");
            if (!StringUtil.isNull(body)) {
                StringEntity s = new StringEntity(body);
                s.setContentEncoding(BaseConstants.UTF8);
                s.setContentType("application/json");
                if (StringUtil.isNullOrEmpty(encoding)) {
                    request.setEntity(s);
                } else {
                    request.setEntity(new StringEntity(body, StandardCharsets.UTF_8));
                }
            }

            return analysisHttpResponse(httpClient.execute(request));
        } catch (IOException e) {
            throw new BusinessException(NO_Q);
        }
    }

    /**
     * Post stream
     *
     * @param url     服务路径
     * @param queries 请求参数
     * @param body    字节数组参数body体
     * @return BaseResult
     */
    public BaseResult doPost(String url, Map<String, Object> queries, byte[] body) {
        try {
            HttpClient httpClient = wrapClient(url);
            HttpPost request = new HttpPost(UrlUtil.buildUrl(url, queries));
            if (!StringUtil.isNull(headers)) {
                for (Map.Entry<String, String> e : headers.entrySet()) {
                    request.addHeader(e.getKey(), e.getValue());
                }
            }
            if (!StringUtil.isNull(body)) {
                request.setEntity(new ByteArrayEntity(body));
            }

            return analysisHttpResponse(httpClient.execute(request));
        } catch (IOException e) {
            throw new BusinessException(NO_Q);
        }
    }

    /**
     * POST请求
     *
     * @param url     主机
     * @param fileMap 文件集合
     * @return 结果
     */
    public BaseResult doFilePost(String url, Map<String, File> fileMap) {
        return doFilePost(url, null, null, fileMap);
    }

    /**
     * POST请求
     *
     * @param url     主机
     * @param queries 条件
     * @param fileMap 文件集合
     * @return 结果
     */
    public BaseResult doFilePost(String url, Map<String, Object> queries, Map<String, File> fileMap) {
        return doFilePost(url, queries, null, fileMap);
    }

    /**
     * POST请求
     *
     * @param url     主机
     * @param queries 条件
     * @param bodies  Body
     * @param fileMap 文件集合
     * @return 结果
     */
    public BaseResult doFilePost(String url, Map<String, Object> queries, Map<String, Object> bodies,
                                 Map<String, File> fileMap) {

        try {
            CloseableHttpClient httpClient = wrapClient(url);

            HttpPost request = new HttpPost(UrlUtil.buildUrl(url, queries));

            MultipartEntityBuilder meBuilder = MultipartEntityBuilder.create();

            if (null != bodies) {
                for (Map.Entry<String, Object> m : bodies.entrySet()) {
                    meBuilder.addPart(m.getKey(), new StringBody(String.valueOf(m.getValue()), ContentType.TEXT_PLAIN));
                }
            }
            if (ObjectUtil.isNotEmpty(fileMap)) {
                for (Map.Entry<String, File> m : fileMap.entrySet()) {
                    FileBody fileBody = new FileBody(m.getValue());
                    meBuilder.addPart(m.getKey(), fileBody);
                }
            }

            HttpEntity reqEntity = meBuilder.build();
            request.setEntity(reqEntity);
            return analysisHttpResponse(httpClient.execute(request));
        } catch (IOException e) {
            throw new BusinessException(NO_Q);
        }
    }

    private CloseableHttpClient wrapClient(String url) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        if (url.startsWith(A)) {
        }

        return httpClient;
    }

    public BaseResult analysisHttpResponse(HttpResponse response) throws IOException {
        return analysisHttpResponse(response, BaseConstants.UTF8);
    }

    public BaseResult analysisHttpResponse(HttpResponse response, String charset) throws IOException {
        StatusLine statusLine = response.getStatusLine();
        if (statusLine.getStatusCode() == BaseConstants.STATUS_OK) {
            HttpEntity entity = response.getEntity();
            if (StringUtil.isNull(entity.getContentType())) {
                return BaseResult.ok(EntityUtils.toByteArray(entity));
            }
            if (entity.getContentType().getValue().startsWith("image/")) {
                return BaseResult.ok(EntityUtils.toByteArray(entity));
            }
            return BaseResult.ok(EntityUtils.toString(entity, charset));
        } else {
            HttpEntity entity = response.getEntity();
            StatusEnum statusEnum = StatusEnum.getStatusEnum(statusLine.getStatusCode());
            if (!StringUtil.isNull(statusEnum)) {
                return BaseResult.error(statusLine.getStatusCode(), String.format("接口请求错误：%s（%s）", statusLine.getReasonPhrase(), statusEnum.getMessage()), EntityUtils.toString(entity, charset));
            } else {
                return BaseResult.error(statusLine.getStatusCode(), String.format("接口请求错误：%s", statusLine.getReasonPhrase()), EntityUtils.toString(entity, charset));
            }
        }
    }

    public void setHeaders(Map<String, String> headers) {
        this.headers = headers;
    }

    public Map<String, String> getHeaders() {
        return headers;
    }

}
